﻿using DevelopAssistant.Common;
using Microsoft.VisualStudio.TextTemplating;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text; 
using System.Xml;

namespace DevelopAssistant.Service.TemplatingEngine
{
    /// <summary>
    /// 代码引擎
    /// </summary>
    [Serializable]
    public class TextTemplatingEngine : ITextTemplatingEngineHost
    {
        private string _pre_DbType = "";
        private string _pre_Parameter = "@";

        static string datatypefile = AppDomain.CurrentDomain.BaseDirectory + @"datatype.ini";
        static INIFile datatype;

        private string _namespace = "DevelopAssistant";
        /// <summary>
        /// 一级命名空间
        /// </summary>
        public string NameSpace
        {
            get { return _namespace; }
            set { _namespace = value; }
        }

        private string _spacename = "Models";
        public string SpaceName
        {
            get { return _spacename; }
            set { _spacename = value; }
        }

        protected string _classname = "tablename";
        /// <summary>
        /// 二级命名空间
        /// </summary>
        public string ClassName
        {
            get
            {
                return _classname;
            }
            set
            {
                _classname = value;
                if (_classname.Contains("："))
                {
                    _classname = _classname.Substring(_classname.LastIndexOf("：") + 1);
                }
                if (_classname.Contains(":"))
                {
                    _classname = _classname.Substring(_classname.LastIndexOf(":") + 1);
                }
            }
        }

        /// <summary>
        /// 类名 (实体名)
        /// </summary>
        public string TTClassName
        {
            get
            {

                string val = string.Empty;
                if (!string.IsNullOrEmpty(_namespace))
                {
                    val += _namespace;
                }
                if (!string.IsNullOrEmpty(_spacename))
                {
                    val += "." + _spacename;
                }
                return val;

            }
        }

        private string _updatedate = "";
        public string UpdateDate
        {
            get
            {
                _updatedate = DateTime.Now.ToString("yyyy-MM-dd HH:mm");
                return _updatedate;
            }
        }

        protected ColumnInfo _pk_column = new ColumnInfo();
        public ColumnInfo Pk_Column
        {
            set { _pk_column = value; }
            get { return _pk_column; }
        }

        protected DataTable _table = new DataTable();
        public DataTable Table
        {
            get { return _table; }
            set { _table = value; }
        }

        protected DbTypes _dtype = DbTypes.Sql;
        public DbTypes DType
        {
            get { return _dtype; }
            set { _dtype = value; }
        }

        protected DbTypes _totype = DbTypes.Sql;
        /// <summary>
        /// 转换成数据库类型
        /// </summary>
        public DbTypes ToType
        {
            set
            {
                _totype = value;

                switch (_totype)
                {
                    case DbTypes.Sql:
                        _pre_Parameter = "@";
                        _pre_DbType = "SqlDbType";
                        break;
                    case DbTypes.MySql:
                        _pre_Parameter = "@";
                        _pre_DbType = "MySqlDbType";
                        break;
                    case DbTypes.OleDb:
                        _pre_Parameter = "@";
                        _pre_DbType = "OleDbType";
                        break;
                    case DbTypes.SqlLite:
                        _pre_Parameter = "@";
                        _pre_DbType = "DbType";
                        break;
                    case DbTypes.Oracle:
                        _pre_Parameter = ":";
                        _pre_DbType = "OracleType";
                        break;
                    default:
                        _pre_Parameter = "@";
                        break;
                }

            }
            get { return _totype; }
        }

        //private DataBaseServer _server;
        ///// <summary>
        ///// 
        ///// </summary>
        //public DataBaseServer Server
        //{
        //    get { return _server; }
        //    set { _server = value; }
        //}

        private string _connectionString;
        public string ConnectionString
        {
            get { return _connectionString; }
            set { _connectionString = value; }
        }

        private string _providerName;
        public string ProviderName
        {
            get { return _providerName; }
            set { _providerName = value; }
        }

        public TextTemplatingEngine()
        {

        }

        #region  动态根据DbTypes 类型添加不同的命名空间
        public string UsingNameSpaceByDbTypes()
        {
            string references_namespace = "";
            switch (this._totype)
            {
                case DbTypes.Sql: references_namespace = "using System.Data.SqlClient;"; break;
                case DbTypes.Oracle: references_namespace = "using System.Data.OracleClient;"; break;
                case DbTypes.MySql: references_namespace = "using MySql.Data.MySqlClient;"; break;
                case DbTypes.OleDb: references_namespace = "using System.Data.OleDb;"; break;
                case DbTypes.SqlLite: references_namespace = "using System.Data.SQLite;"; break;
            }
            return references_namespace;
        }
        #endregion

        #region 转换 数据库字段类型 为 c#类型

        /// <summary>
        /// 转换【数据库字段类型】 =》为【c#类型】
        /// </summary>
        /// <param name="dbtype">数据库字段类型</param>
        /// <returns>c#类型</returns>		
        public string DbTypeToCS(object dbtype)
        {
            string DbType = dbtype + "";
            string CSType = "string";
            if (File.Exists(datatypefile))
            {
                datatype = new INIFile(datatypefile);
                string val = datatype.IniReadValue("DbToCS", DbType.ToLower().Trim());
                if (val == "")
                {
                    CSType = DbType.ToLower().Trim();
                }
                else
                {
                    CSType = val;
                }
            }
            return CSType;

        }
        public string DbTypeToCS(object dbtype, object cisnull)
        {
            string DbType = dbtype + "";
            string CSType = "string";
            if (File.Exists(datatypefile))
            {
                datatype = new INIFile(datatypefile);
                string val = datatype.IniReadValue("DbToCS", DbType.ToLower().Trim());
                if (val == "")
                {
                    CSType = DbType.ToLower().Trim();
                }
                else
                {
                    CSType = val;
                }
                string colisnull = cisnull + "";
                if (!colisnull.Contains("not null") && CSType != "string")
                {
                    CSType += "?";
                }
            }
            return CSType;

        }
        #endregion

        #region 数据库类型转换

        #region CSToProcType

        private string CSToProcTypeSQL(string cstype)
        {
            string CSType = cstype;
            if (File.Exists(datatypefile))
            {
                datatype = new INIFile(datatypefile);
                string val = datatype.IniReadValue("ToSQLProc", cstype.ToLower().Trim());
                if (val == "")
                {
                    CSType = cstype.ToLower().Trim();
                }
                else
                {
                    CSType = val;
                }
            }
            return CSType;
        }

        private string CSToProcTypeOra(string cstype)
        {
            string CSType = cstype;
            if (File.Exists(datatypefile))
            {
                datatype = new INIFile(datatypefile);
                string val = datatype.IniReadValue("ToOraProc", cstype.ToLower().Trim());
                if (val == "")
                {
                    CSType = cstype.ToLower().Trim();
                }
                else
                {
                    CSType = val;
                }
            }
            return CSType;
        }

        private string CSToProcTypeMySQL(string cstype)
        {
            string CSType = cstype;
            if (File.Exists(datatypefile))
            {
                datatype = new INIFile(datatypefile);
                string val = datatype.IniReadValue("ToMySQLProc", cstype.ToLower().Trim());
                if (val == "")
                {
                    CSType = cstype.ToLower().Trim();
                }
                else
                {
                    CSType = val;
                }
            }
            return CSType;
        }

        private string CSToProcTypeOleDb(string cstype)
        {
            string CSType = cstype;
            if (File.Exists(datatypefile))
            {
                datatype = new INIFile(datatypefile);
                string val = datatype.IniReadValue("ToOleDbProc", cstype.ToLower().Trim());
                if (val == "")
                {
                    CSType = cstype.ToLower().Trim();
                }
                else
                {
                    CSType = val;
                }
            }
            return CSType;
        }

        private string CSToProcTypeSqlLite(string cstype)
        {
            string CSType = cstype;
            if (File.Exists(datatypefile))
            {
                datatype = new INIFile(datatypefile);
                string val = datatype.IniReadValue("ToSqlLiteProc", cstype.ToLower().Trim());
                if (val == "")
                {
                    CSType = cstype.ToLower().Trim();
                }
                else
                {
                    CSType = val;
                }
            }
            return CSType;
        }

        #endregion

        /// <summary>
        /// 转换c#类型和数据类型转为存储过程的参数类型
        /// </summary>
        /// <param name="dbtype">数据库字段类型</param>
        /// <returns>c#类型</returns>
        public string CSToProcType(string DbType, string cstype)
        {
            string strtype = cstype;
            switch (DbType)
            {
                case "SQL2000":
                case "SQL2005":
                    strtype = CSToProcTypeSQL(cstype);
                    break;
                case "Oracle":
                    strtype = CSToProcTypeOra(cstype);
                    break;
                case "MySQL":
                    strtype = CSToProcTypeMySQL(cstype);
                    break;
                case "OleDb":
                    strtype = CSToProcTypeOleDb(cstype);
                    break;
                case "SqlLite":
                    strtype = CSToProcTypeSqlLite(cstype);
                    break;
            }
            return strtype;
        }

        #endregion

        #region 将表字段转换成一行

        public string TableColumnsToLine(DataTable dt)
        {
            string strline = "";
            foreach (DataRow dr in dt.Rows)
            {
                if (dr["ColumnName"].ToString() != _pk_column.ColumnName)
                {
                    strline += "" + dr["ColumnName"] + ",";
                }
            }
            if (strline.Contains(","))
            {
                strline = strline.Trim(',');
            }
            return strline;
        }

        public string TableColumnsToParameterLine(DataTable dt)
        {
            string strline = "";
            foreach (DataRow dr in dt.Rows)
            {
                if (dr["ColumnName"].ToString() != _pk_column.ColumnName)
                {
                    strline += "" + _pre_Parameter + "" + dr["ColumnName"] + ",";
                }
            }
            if (strline.Contains(","))
            {
                strline = strline.Trim(',');
            }
            return strline;
        }

        #endregion

        #region 转换表字段的Parameter
        public string ToFirstUpper(string typename)
        {
            string v = "";
            typename = typename.ToLower();
            char[] chr_array = typename.ToCharArray();
            for (int i = 0; i < chr_array.Length; i++)
            {
                if (i == 0)
                {
                    v += chr_array[i].ToString().ToUpper();
                }
                else
                {
                    v += chr_array[i].ToString();
                }
            }
            return v;
        }
        public string ToDbTypes(string typename)
        {
            switch (this._totype)
            {
                case DbTypes.Sql:
                    typename = CSToProcType("SQL2005", typename);
                    break;
                case DbTypes.Oracle:
                    typename = CSToProcType("Oracle", typename);
                    break;
                case DbTypes.MySql:
                    typename = CSToProcType("MySQL", typename);
                    break;
                case DbTypes.OleDb:
                    typename = CSToProcType("OleDb", typename);
                    break;
                case DbTypes.SqlLite:
                    typename = CSToProcType("SqlLite", typename);
                    break;
            }
            return typename;
        }
        public string TableColumnsToParameter(DataRow dr, int Index)
        {
            string val = "";
            string len = dr["Length"] + "";
            string typename = dr["TypeName"] + "";
            typename = ToDbTypes(typename.ToLower());
            if (string.IsNullOrEmpty(len))
            {
                val = string.Format("new {0}Parameter(\"{1}{2}\", {3}.{4})", this._totype, _pre_Parameter, dr["ColumnName"], _pre_DbType, typename);
            }
            else if (len == "max")
            {
                val = string.Format("new {0}Parameter(\"{1}{2}\", {3}.{4})", this._totype, _pre_Parameter, dr["ColumnName"], _pre_DbType, typename);
            }
            else if (typename == "Real" || typename == "Bit")
            {
                val = string.Format("new {0}Parameter(\"{1}{2}\", {3}.{4})", this._totype, _pre_Parameter, dr["ColumnName"], _pre_DbType, typename);
            }
            else if (typename.StartsWith("N") || typename.StartsWith("n"))
            {
                len = (Int32.Parse(len) / 2).ToString();
                val = string.Format("new {0}Parameter(\"{1}{2}\", {3}.{4},{5})", this._totype, _pre_Parameter, dr["ColumnName"], _pre_DbType, typename, len);
            }
            else
            {
                val = string.Format("new {0}Parameter(\"{1}{2}\", {3}.{4},{5})", this._totype, _pre_Parameter, dr["ColumnName"], _pre_DbType, typename, dr["Length"]);
            }
            if (Index < dr.Table.Rows.Count - 1)
            {
                val += ",";
            }
            return val;
        }
        #endregion

        #region 获取表列名与参数的对应
        public string ToColumnParameter(DataRow dr, int index)
        {
            string val = "";
            if (index < dr.Table.Rows.Count - 1)
            {
                val = "" + dr["ColumnName"] + "=" + _pre_Parameter + "" + dr["ColumnName"] + ",";
            }
            else
            {
                val = "" + dr["ColumnName"] + "=" + _pre_Parameter + "" + dr["ColumnName"] + "";
            }
            return val;
        }
        #endregion

        #region 获取给Model赋值的对应
        public string ToColumnValue(DataRow dr)
        {
            string val = "";
            string cstype = DbTypeToCS(dr["TypeName"]);
            if (cstype != "string")
            {
                val = "model." + dr["ColumnName"] + "=" + cstype + ".Parse(row[\"" + dr["ColumnName"] + "\"].ToString());";
            }
            else
            {
                val = "model." + dr["ColumnName"] + "=row[\"" + dr["ColumnName"] + "\"].ToString();";
            }
            return val;
        }
        #endregion

        protected string _templateFile = "";
        public string TemplateFile
        {
            get { return _templateFile; }
            set { _templateFile = value; }
        }
        protected string _fileExtension = ".cs";
        public string FileExtension
        {
            get { return _fileExtension; }
            set { _fileExtension = value; }
        }
        public IList<string> StandardAssemblyReferences
        {
            get
            {
                return new string[]
                {
                        typeof(System.Uri).Assembly.Location,
                        typeof(TextTemplatingEngine).Assembly.Location,
                        typeof(DbType).Assembly.Location,
                        typeof(XmlDataDocument).Assembly.Location,
                        typeof(System.Xml.Serialization.XmlSchemas).Assembly.Location,
                        typeof(System.AppDomain ).Assembly.Location 
                };
            }
        }
        public IList<string> StandardImports
        {
            get
            {
                return new string[]{ 
                    "System",
                    "System.IO",
                    "System.Xml",
                    "System.Xml.Serialization",
                    "System.Data",
                    "System.Data.Common",
                    "System.Text",
                    "System.Collections",
                    "System.Collections.Generic",
                    "System.Collections.Specialized",
                    "DevelopAssistant.Service.TemplatingEngine" 
                };

            }
        }

        public object GetHostOption(string optionName)
        {
            object returnObject;
            switch (optionName)
            {
                case "CacheAssemblies":
                    returnObject = true;
                    break;
                default:
                    returnObject = null;
                    break;
            }
            return returnObject;
        }

        public void SetFileExtension(string extension)
        {
            _fileExtension = extension;
        }

        private System.Text.Encoding _fileEncodingValue = System.Text.Encoding.UTF8;
        public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective)
        {
            _fileEncodingValue = encoding;
        }

        private CompilerErrorCollection _errorsValue;
        public CompilerErrorCollection Errors
        {
            get { return _errorsValue; }
        }

        public void LogErrors(CompilerErrorCollection errors)
        {
            _errorsValue = errors;
        }
        public string ResolvePath(string fileName)
        {
            return fileName;
        }
        public bool LoadIncludeText(string requestFileName, out string content, out string location)
        {
            content = System.String.Empty;
            location = System.String.Empty;

            //If the argument is the fully qualified path of an existing file,
            //then we are done.
            //----------------------------------------------------------------
            if (File.Exists(requestFileName))
            {
                content = File.ReadAllText(requestFileName);
                return true;
            }

            //This can be customized to search specific paths for the file.
            //This can be customized to accept paths to search as command line
            //arguments.
            //----------------------------------------------------------------
            else
            {
                return false;
            }
        }
        public string ResolveAssemblyReference(string assemblyReference)
        {
            if (File.Exists(assemblyReference))
            {
                return assemblyReference;
            }
            string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference);
            if (File.Exists(candidate))
            {
                return candidate;
            }
            return "";
        }
        public Type ResolveDirectiveProcessor(string processorName)
        {
            throw new Exception("Directive Processor not found");
        }
        public string ResolveParameterValue(string directiveId, string processorName, string parameterName)
        {
            if (directiveId == null)
            {
                throw new ArgumentNullException("the directiveId cannot be null");
            }
            if (processorName == null)
            {
                throw new ArgumentNullException("the processorName cannot be null");
            }
            if (parameterName == null)
            {
                throw new ArgumentNullException("the parameterName cannot be null");
            }

            //Code to provide "hard-coded" parameter values goes here.
            //This code depends on the directive processors this host will interact with.

            //If we cannot do better, return the empty string.
            return String.Empty;
        }
        public AppDomain ProvideTemplatingAppDomain(string content)
        {
            //This host will provide a new application domain each time the 
            //engine processes a text template.
            //-------------------------------------------------------------
            return AppDomain.CreateDomain("Generation App Domain");

            //This could be changed to return the current appdomain, but new 
            //assemblies are loaded into this AppDomain on a regular basis.
            //If the AppDomain lasts too long, it will grow indefintely, 
            //which might be regarded as a leak.

            //This could be customized to cache the application domain for 
            //a certain number of text template generations (for example, 10).

            //This could be customized based on the contents of the text 
            //template, which are provided as a parameter for that purpose.
        }
    }
}
